package com.gec.service.workflow;

import org.activiti.bpmn.model.*;
import org.activiti.bpmn.model.Process;
import org.activiti.engine.*;
import org.activiti.engine.form.TaskFormData;
import org.activiti.engine.history.HistoricIdentityLink;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.*;
import java.util.zip.ZipInputStream;

public class WorkFlowServiceImpl implements WorkFlowService {

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private FormService formService;

    @Autowired
    private HistoryService historyService;

    public void setHistoryService(HistoryService historyService) {
        this.historyService = historyService;
    }

    public void setFormService(FormService formService) {
        this.formService = formService;
    }

    public void setRuntimeService(RuntimeService runtimeService) {
        this.runtimeService = runtimeService;
    }

    public void setTaskService(TaskService taskService) {
        this.taskService = taskService;
    }

    public void setRepositoryService(RepositoryService repositoryService) {
        this.repositoryService = repositoryService;
    }

    /*************** 流程设计图相关 ****************/
    // 发布流程图(上传的文件,名字-不是特别重要,自己起)
    // 保存设计文件和图片到数据库中
    @Override
    public void saveNewDeploye(InputStream input, String name) {
        try {
            ZipInputStream zipInputStream = new ZipInputStream(input);
            repositoryService.createDeployment().name(name).addZipInputStream(zipInputStream).deploy();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 保存设计文件和图片到数据库中
    @Override
    public void saveNewDeploye(File file, String filename) {
        try {

            ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(file));
            repositoryService.createDeployment().name(filename).addZipInputStream(zipInputStream).deploy();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 查找流程发布列表
    @Override
    public List<Deployment> findDeploymentList() {
        List<Deployment> list = repositoryService.createDeploymentQuery().orderByDeploymenTime().asc()//
                .list();
        return list;
    }

    // 查找流程图信息用于显示图片（根据发布ID和图片名称）
    @Override
    public InputStream findImageInputStream(String deploymentId, String imageName) {
        return repositoryService.getResourceAsStream(deploymentId, imageName);
    }

    // 根据流程key值和绑定的业务key值，查找流程图，用于显示流程图
    @Override
    public InputStream findImageInputStreamByBizkey(String processDefineKey, String bizkey) {
        ProcessInstance pi = this.findProcessInstanceByBizkey(processDefineKey, bizkey);
        if (pi == null)
            return null;
        String deploymentId = pi.getDeploymentId();
        String imageName = this.repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId)
                .singleResult().getDiagramResourceName();
        return this.findImageInputStream(deploymentId, imageName);
    }

    // 删除流程图
    @Override
    public void deleteProcessDefinitionByDeploymentId(String deploymentId) {
        repositoryService.deleteDeployment(deploymentId, true);
    }

    /**
     * 二：查看当前活动，获取当前活动对应的坐标x,y,width,height，将4个值存放到Map<String,Object>中
     * map集合的key：表示坐标x,y,width,height map集合的value：表示坐标对应的值
     */
    @Override
    public List<Map<String, Object>> findCoordingByBizkey(String processDefineKey, String bizkey, String taskId) {

        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();

        ProcessInstance pi = this.findProcessInstanceByBizkey(processDefineKey, bizkey);
        if (pi == null)
            return list;

        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();

        //获取任务实例
        //Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();

        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());

        //获取当前活动对象
        FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey());

        GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowElement.getId());

        Map<String, Object> map = new HashMap<>();

        map.put("x", graphicInfo.getX());
        System.out.println(graphicInfo.getX());

        map.put("y", graphicInfo.getY());
        System.out.println(graphicInfo.getY());

        map.put("width", graphicInfo.getWidth());
        System.out.println(graphicInfo.getWidth());

        map.put("height", graphicInfo.getHeight());
        System.out.println(graphicInfo.getHeight());

        list.add(map);

        return list;
    }

    // 查找某个任务的输出连线的名称（用于动态变化按钮的名称)
    @Override
    public List<String> findOutComeListByTaskId(String taskId) {

        // 返回存放连线的名称集合
        List<String> list = new ArrayList<String>();
        // 1:使用任务ID，查询任务对象
        Task task = taskService.createTaskQuery()//
                .taskId(taskId)// 使用任务ID查询
                .singleResult();
        // 2：获取流程定义ID
        String processDefinitionId = task.getProcessDefinitionId();

        //获取当前bpmn流程图模型
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);

        //获取当前活动对象
        FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey());

        if (flowElement instanceof UserTask) {
            //如果是任务节点
            UserTask userTask = (UserTask) flowElement;
            //获取当前任务的输出连线
            List<SequenceFlow> outgoingFlows = userTask.getOutgoingFlows();

            for (SequenceFlow sequenceFlow : outgoingFlows) {

                if (sequenceFlow.getSourceRef().equals(flowElement.getId())) {
                    //如果输出源为当前任务id

                    //获取连线的名称
                    String name = sequenceFlow.getName();

                    if (StringUtils.isNotBlank(name)) {
                        //如果名字不为空
                        //依次放入集合
                        list.add(name);
                    } else {
                        list.add("默认提交");
                    }
                }
            }
        }
        return list;
    }

    /*** 获取流程定义ProcessDefinition相关 ***/
    // 查找流程定义列表
    @Override
    public List<ProcessDefinition> findProcessDefinitionList() {
        List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery()
                .orderByProcessDefinitionVersion().asc()//
                .list();
        return list;
    }

    // 根据任务ID，获取任务对象，使用任务对象获取流程定义ID，查询流程定义对象
    @Override
    public ProcessDefinition findProcessDefinitionByTaskId(String taskId) {
        // 使用任务ID，查询任务对象
        Task task = taskService.createTaskQuery()//
                .taskId(taskId)// 使用任务ID查询
                .singleResult();
        // 获取流程定义ID
        String processDefinitionId = task.getProcessDefinitionId();
        // 查询流程定义的对象
        ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()// 创建流程定义查询对象，对应表act_re_procdef
                .processDefinitionId(processDefinitionId)// 使用流程定义ID查询
                .singleResult();
        return pd;
    }

    /********** 流程实例ProcessInstance相关 ************/
    // 启动一个工作流实例(发起一个按某个图纸规定的流程,每个申请单就要发起一个流程实例)
    @Override
    public ProcessInstance saveStartProcess(String processkey, String bizkey, Map<String, Object> variables) {
        ProcessInstance procins = runtimeService.startProcessInstanceByKey(processkey, bizkey, variables);
        return procins;

    }

    // 根据业务key和流程的key来查找实例（因为业务key的值可能重复，可能是不同表相同的id)
    @Override
    public ProcessInstance findProcessInstanceByBizkey(String processDefineKey, String bizkey) {
        ProcessInstance pi = this.runtimeService.createProcessInstanceQuery().processDefinitionKey(processDefineKey)
                .processInstanceBusinessKey(bizkey).singleResult();
        return pi;
    }

    /************************* 任务Task相关 ****************************/
    // 根据任务id查找任务
    @Override
    public Task findTaskById(String taskId) {

        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        return task;
    }

    // 根据流程实例id查找当前正在执行的任务
    @Override
    public Task findTaskByProcessInstId(String processInstid) {
        Task task = taskService.createTaskQuery()//
                .processInstanceId(processInstid).singleResult();
        return task;
    }

    // 根据操作人查找任务列表
    @Override
    public List<Task> findTaskListByName(String userkey) {
        List<Task> list = taskService.createTaskQuery().taskAssignee(userkey).orderByTaskCreateTime().asc().list();
        return list;
    }

    // 查找任务列表，根据流程定义的key值和操作人查找任务列表(用流程定义key值分辨是哪种流程)
    @Override
    public List<Task> findTaskListByName(String processkey, String name) {

        List<Task> list = taskService.createTaskQuery().taskAssignee(name).processDefinitionKey(processkey)
                .orderByTaskCreateTime().asc().list();
        return list;
    }

    // 查找组任务列表，根据流程定义的key值和操作人查找任务列表(用流程定义key值分辨是哪种流程)
    @Override
    public List<Task> findGroupTaskListByName(String processkey, String name) {

        List<Task> list = taskService.createTaskQuery().taskCandidateUser(name).processDefinitionKey(processkey)
                .orderByTaskCreateTime().asc().list();
        return list;
    }

    // 根据任务id查找任务的formkey
    @Override
    public String findTaskFormKeyByTaskId(String taskId) {
        TaskFormData formData = formService.getTaskFormData(taskId);
        String url = formData.getFormKey();
        return url;
    }

    // 根据任务查找绑定业务的key值
    @Override
    public String findBizKeyByTask(Task task) {
        String processInstanceId = task.getProcessInstanceId();
        ProcessInstance pi = runtimeService.createProcessInstanceQuery()//
                .processInstanceId(processInstanceId).singleResult();
        String bizkey = pi.getBusinessKey();
        return bizkey;
    }

    // 根据任务id查找绑定业务的key值
    @Override
    public String findBizKeyByTaskId(String taskId) {
        System.out.println("taskid:" + taskId);
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        String processInstanceId = task.getProcessInstanceId();
        ProcessInstance pi = runtimeService.createProcessInstanceQuery()//
                .processInstanceId(processInstanceId).singleResult();
        String bizkey = pi.getBusinessKey();
        return bizkey;
    }

    // 结束一个任务，包括领取组任务，添加批注信息，结束任务，返回下一个任务
    @Override
    public Task saveSubmitTask(String taskId, String currentuser, String message, Map<String, Object> variables) {

        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task.getAssignee() == null || task.getAssignee().isEmpty())// 如果是组任务，则assignee属性为空
            this.Group2User(taskId, currentuser);// 如果是组任务，当前用户领取这个任务，则设置当前用户为操作人

        String processInstanceId = task.getProcessInstanceId();
        // 记录批注信息在特定的表
        if (message != null && !message.isEmpty()) {
            Authentication.setAuthenticatedUserId(currentuser);// 录入批注人是谁 2_王武
            taskService.addComment(taskId, processInstanceId, message);
        }
        // 结束任务
        taskService.complete(taskId, variables);// 传入相应的变量，指引下一步的方向，或者是供后续任务使用

        // 查询下一个任务是谁(可能你需要知道下一个任务的名称...)
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId)
                .singleResult();
        if (pi == null)
            return null;
        return this.findTaskByProcessInstId(pi.getId());
    }

    // 根据绑定的业务key值查找当前的任务ID列表(可能并行的，可能会有多个)
    @Override
    public List<String> findCurrentTasksByBizKey(String processDefineKey, String bizkey) {
        List<String> list = new ArrayList<String>();
        List<Execution> exlist = this.runtimeService.createExecutionQuery().processDefinitionKey(processDefineKey)
                .processInstanceBusinessKey(bizkey).list();
        if (exlist.size() == 0)
            return list;
        for (Execution ex : exlist) {
            list.add(ex.getActivityId());
        }
        return list;
    }

    /************************** 和批注相关 ******************************/
    // 获取批注信息，传递的是当前任务ID，获取历史任务ID对应的批注
    @Override
    public List<Comment> findCommentByTaskId(String taskId) {
        List<Comment> list = new ArrayList<Comment>();
        // 使用当前的任务ID，查询当前流程对应的历史任务ID
        // 使用当前任务ID，获取当前任务对象
        Task task = taskService.createTaskQuery()//
                .taskId(taskId)// 使用任务ID查询
                .singleResult();
        // 获取流程实例ID
        String processInstanceId = task.getProcessInstanceId();
        // //使用流程实例ID，查询历史任务，获取历史任务对应的每个任务ID
        // List<HistoricTaskInstance> htiList =
        // historyService.createHistoricTaskInstanceQuery()//历史任务表查询
        // .processInstanceId(processInstanceId)//使用流程实例ID查询
        // .list();
        // //遍历集合，获取每个任务ID
        // if(htiList!=null && htiList.size()>0){
        // for(HistoricTaskInstance hti:htiList){
        // //任务ID
        // String htaskId = hti.getId();
        // //获取批注信息
        // List<Comment> taskList =
        // taskService.getTaskComments(htaskId);//对用历史完成后的任务ID
        // list.addAll(taskList);
        // }
        // }
        list = taskService.getProcessInstanceComments(processInstanceId);
        return list;
    }

    // 根据绑定的业务ID（不重复时)，查询历史批注信息 */
    @Override
    public List<Comment> findCommentByBizKey(String bizkey) {

        /** 1:使用历史的流程实例查询，返回历史的流程实例对象，获取流程实例ID */
        HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery()// 对应历史的流程实例表
                .processInstanceBusinessKey(bizkey)// 使用BusinessKey字段查询
                .singleResult();
        // //流程实例ID
        String processInstanceId = hpi.getId();
        /** 2:使用历史的流程变量查询，返回历史的流程变量的对象，获取流程实例ID */
        /*
         * HistoricVariableInstance hvi =
         * historyService.createHistoricVariableInstanceQuery()//对应历史的流程变量表
         * .variableValueEquals("objId", key)//使用流程变量的名称和流程变量的值查询 .singleResult();
         * //流程实例ID String processInstanceId = hvi.getProcessInstanceId();
         */
        List<Comment> list = taskService.getProcessInstanceComments(processInstanceId);
        return list;
    }

    // 根据绑定的流程定义ID+业务ID，查询历史批注信息
    @Override
    public List<Comment> findCommentByBizKey(String processkey, String bizkey) {

        /** 1:使用历史的流程实例查询，返回历史的流程实例对象，获取流程实例ID */
        HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery()// 对应历史的流程实例表
                .processDefinitionKey(bizkey).processInstanceBusinessKey(bizkey)// 使用BusinessKey字段查询
                .singleResult();
        // //流程实例ID
        String processInstanceId = hpi.getId();
        /** 2:使用历史的流程变量查询，返回历史的流程变量的对象，获取流程实例ID */
        /*
         * HistoricVariableInstance hvi =
         * historyService.createHistoricVariableInstanceQuery()//对应历史的流程变量表
         * .variableValueEquals("objId", key)//使用流程变量的名称和流程变量的值查询 .singleResult();
         * //流程实例ID String processInstanceId = hvi.getProcessInstanceId();
         */
        List<Comment> list = taskService.getProcessInstanceComments(processInstanceId);
        return list;
    }

    /******** 和人员相关 ********/

    // 根据任务id判断是否组任务
    public boolean isGroupUser(String taskId) {
        return this.isGroupUser(this.findTaskById(taskId));
    }

    // 根据任务对象判断是否组任务
    public boolean isGroupUser(Task task) {
        return task.getAssignee() == null;// 如果assignee为空则应该为组任务(或者是你的疏忽)
    }

    // 领取任务，将组任务领取为个人任务
    @Override
    public void Group2User(String taskid, String currentuser) {
        if (isGroupUser(taskid))// 如果是组任务
            this.taskService.claim(taskid, currentuser);// 则领取这个任务
    }

    // 取消领取任务，将个人任务撤退会组任务
    @Override
    public void User2Group(String taskid, String currentuser) {
        this.taskService.setAssignee(taskid, null);// 把assignee设置为null，则返回组任务，别人可以领取
    }

    // 查看组任务成员列表
    @Override
    public List<IdentityLink> findGroupUsers(String taskid) {
        List<IdentityLink> list = this.taskService.getIdentityLinksForTask(taskid);// 获取列表
        return list;
    }

    // 查询组任务成员历史列表
    @Override
    public List<HistoricIdentityLink> findHisGroupUsers(String taskid) {

        List<HistoricIdentityLink> list = this.historyService.getHistoricIdentityLinksForTask(taskid);
        return list;
    }
}
